package com.fausto.learnimprint.utils

import cn.hutool.core.date.DateTime
import com.fausto.learnimprint.common.constant.JwtConstant.CLAIM_KEY_CREATED
import com.fausto.learnimprint.common.constant.JwtConstant.CLAIM_KEY_ROLES
import com.fausto.learnimprint.common.constant.JwtConstant.CLAIM_KEY_USERNAME
import com.fausto.learnimprint.common.constant.JwtConstant.EXPIRED_TIME
import com.fausto.learnimprint.common.constant.JwtConstant.HEADER
import com.fausto.learnimprint.common.constant.JwtConstant.PREFIX
import com.fausto.learnimprint.common.constant.JwtConstant.SECRET
import com.fausto.learnimprint.common.lang.Option
import com.fausto.learnimprint.common.lang.Option.None
import com.fausto.learnimprint.common.lang.Option.Some
import com.fausto.learnimprint.core.model.entity.User
import io.jsonwebtoken.Claims
import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.stereotype.Component
import java.io.Serializable
import java.util.*
import javax.servlet.http.HttpServletRequest


@Component
class JwtUtil : Serializable {
    companion object {
        private const val serialVersionUID = -5625635588908941275L
    }


    /**
     *      从token中获取用户信息
     */

    fun loadUserByToken(token: String): User {

        //获取用户名
        val username: String
        when (val option = getUsernameFromToken(token)) {
            //无token, 赋予游客身份
            is Option.None -> {
                return User(
                    "GUEST",
                    "GUEST",
                    listOf(SimpleGrantedAuthority("GUEST"))
                )
            }
            //有token, 返回用户信息
            is Option.Some -> username = option.unwrap()
        }
        //获取角色信息
        val authorities = ArrayList<SimpleGrantedAuthority>()

        getRolesFromToken(token).forEach {
            authorities.add(SimpleGrantedAuthority(it))
        }
        //返回信息
        return User(
            username,
            "***********",
            authorities
        )
    }

    /**
     *      生成Token
     */
    fun generateToken(userDetails: UserDetails): String {
        val claims = HashMap<String, Any>(3)

        claims[CLAIM_KEY_USERNAME] = userDetails.username
        claims[CLAIM_KEY_CREATED] = DateTime.now()

        val roleList = ArrayList<String>()
        //获取权限信息
        userDetails.authorities.forEach {
            roleList.add(it.authority)
        }
        claims[CLAIM_KEY_ROLES] = roleList
        //返回用户名, 用户创建时间和权限信息
        return generateToken(claims)
    }

    private fun generateToken(claims: Map<String, Any>): String {
        return Jwts.builder()
            .setClaims(claims)
            .setExpiration(Calendar.getInstance().apply {
                add(Calendar.DATE, EXPIRED_TIME)
            }.time)
            .signWith(SignatureAlgorithm.HS512, SECRET)
            .compact()
    }

    /**
     *      校验Token
     */
    fun validateToken(token: String, userDetails: UserDetails): Boolean {
        return getUsernameFromToken(token).unwrap() == userDetails.username
                && isTokenExpired(getClaimsFromToken(token).unwrap())
    }

    /**
     *      刷新Token
     *          为None时刷新失败
     */
    fun refreshToken(token: String): Option<String> {

        val claims = getClaimsFromToken(token).unwrap()

        return if (isTokenExpired(claims)) {
            None()
        } else try {
            val newClaims = claims.apply {
                this[CLAIM_KEY_CREATED] = DateTime.now()
            }
            Some(generateToken(newClaims))
        } catch (e: Exception) {
            None()
        }
    }


    /**
     *      判断Token是否过期
     */
    fun isTokenExpired(claims: Claims): Boolean {
        return claims.expiration.before(DateTime.now())
    }

    /**
     *      从HTTP请求中获取用户名
     */
    fun getTokenFromRequest(request: HttpServletRequest): Option<String> {
        return request.getHeader(HEADER)?.let {
            Some(it.substring(PREFIX.length))
        } ?: None()
    }

    /**
     *      从Token中获取用户名
     */
    fun getUsernameFromToken(token: String): Option<String> {
        return getClaimsFromToken(token).let {
            when (it) {
                is None -> None()
                is Some -> Some(it.content.subject)
            }
        }
    }

    /**
     *      从Token中获取用户名
     */
    fun getUsernameFromRequest(request: HttpServletRequest): Option<String> {
        return when (val option = getTokenFromRequest(request)) {
            is None -> None()
            is Some -> getUsernameFromToken(option.unwrap())
        }

    }

    /**
     *      从Token中获取Claims
     */
    fun getClaimsFromToken(token: String): Option<Claims> {
        return try {
            val claims = Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .body
            Some(claims)
        } catch (e: Exception) {
            None()
        }
    }

    fun getRolesFromToken(token: String): List<String> {

        val claimsOption = getClaimsFromToken(token)

        var roleList = ArrayList<String>()
        try {
            when (claimsOption) {
                //无权限信息则赋予游客权限
                is None -> {
                    roleList = ArrayList<String>(3).apply{
                        add("GUEST")
                    }
                }
                //有角色则返回
                is Some -> {
                    @Suppress("UNCHECKED_CAST")
                    (claimsOption.unwrap()[CLAIM_KEY_ROLES] as ArrayList<String>).forEach {
                        roleList.add(it)
                    }
                }
            }
        } catch (e: Exception) {
            roleList.add("GUEST")
        }
        return roleList
    }
}